﻿using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Reflection;
using Nancy;
using Nancy.Authentication.Stateless;
using Nancy.Bootstrapper;
using Nancy.Conventions;
using Nancy.ErrorHandling;
using Nancy.TinyIoc;
using Nancy.ViewEngines;
using Nancy.Serialization.JsonNet;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Nancy.Session;
using System.Configuration;
using PlatWL.Models;

namespace PlatWL
{
    public class BootStrapper : DefaultNancyBootstrapper
    {
        protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
        {
            base.ConfigureRequestContainer(container, context);

            // Here we register our user mapper as a per-request singleton.
            // As this is now per-request we could inject a request scoped
            // database "context" or other request scoped services.
        }
        protected override void ConfigureConventions(NancyConventions nancyConventions)
        {
            base.ConfigureConventions(nancyConventions);

            nancyConventions.StaticContentsConventions
                .Add(StaticContentConventionBuilder.AddDirectory("static", @"Static"));
            nancyConventions.StaticContentsConventions
              .Add(StaticContentConventionBuilder.AddDirectory("mobile", @"mobile"));
            nancyConventions.StaticContentsConventions
            .Add(StaticContentConventionBuilder.AddDirectory("admin", @"admin"));
            nancyConventions.StaticContentsConventions
              .Add(StaticContentConventionBuilder.AddFile("/MP_verify_yDoY8hCKaZl2P1Pv.txt", @"MP_verify_yDoY8hCKaZl2P1Pv.txt"));

            //nancyConventions.StaticContentsConventions
            //   .Add(StaticContentConventionBuilder.AddDirectory("mobile", @"mobile"));

            //nancyConventions.StaticContentsConventions
            //   .Add(StaticContentConventionBuilder.AddFile("MP_verify_yDoY8hCKaZl2P1Pv.txt", "yDoY8hCKaZl2P1Pv"));

        }

        /// <summary>
        /// Default assemblies that are ignored for autoregister
        /// </summary>
        private new static readonly IEnumerable<Func<Assembly, bool>> DefaultAutoRegisterIgnoredAssemblies = new Func<Assembly, bool>[]
            {
                asm => asm.FullName.StartsWith("Microsoft.", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("System.", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("System,", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("CR_ExtUnitTest", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("mscorlib,", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("CR_VSTest", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("DevExpress.CodeRush", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("IronPython", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("IronRuby", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("xunit", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("Nancy.Testing", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("MonoDevelop.NUnit", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("SMDiagnostics", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("CppCodeProvider", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("WebDev.WebHost40", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("nunit", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("nCrunch", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("EntityFramework", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("Newtonsoft.Json", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("NPOI", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("Senparc.Weixin", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("System.Web.Razor", StringComparison.Ordinal)
            };

        protected override IEnumerable<Func<Assembly, bool>> AutoRegisterIgnoredAssemblies
        {
            get
            {
                return DefaultAutoRegisterIgnoredAssemblies;
            }
        }

        protected override void ConfigureApplicationContainer(TinyIoCContainer container)
        {
            base.ConfigureApplicationContainer(container);

            container.Register<JsonSerializer, CustomJsonSerializer>();

        }

        protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
        {
            base.ApplicationStartup(container, pipelines);
            CookieBasedSessions.Enable(pipelines);

            Nancy.Json.JsonSettings.MaxJsonLength = Int32.MaxValue;
            Nancy.Json.JsonSettings.MaxRecursions = Int32.MaxValue;

            //注册微信API
            //Services.Tools.WXHelp.RegisterWx();

            //Jsonp.Disable(pipelines);//默认开启的Jsonp

            var statelessAuthConfiguration = new StatelessAuthenticationConfiguration(nancyContext =>
            {
                string token = nancyContext.Request.Headers.Authorization;
                if (!string.IsNullOrEmpty(token))
                {
                    return UserMapper.GetUserFromAccessToken(token);
                }
                else
                {
                    return null;
                }
            });
            StatelessAuthentication.Enable(pipelines, statelessAuthConfiguration);

        }

        protected override void RequestStartup(TinyIoCContainer requestContainer, IPipelines pipelines, NancyContext context)
        {
            base.RequestStartup(requestContainer, pipelines, context);


            var maxAge = 21600;
            //CORS Enable
            pipelines.AfterRequest.AddItemToEndOfPipeline((ctx) =>
            {
                if (ctx.Request.Headers.Keys.Contains("Origin"))
                {
                    var origins = "" + string.Join(" ", ctx.Request.Headers["Origin"]);
                    ctx.Response.Headers["Access-Control-Allow-Origin"] = origins;
                    ctx.Response.WithHeader("Access-Control-Allow-Credentials", "true")
                        .WithHeader("Access-Control-Allow-Methods", "POST,GET,PUT,DELETE,OPTIONS")
                        .WithHeader("Access-Control-Allow-Headers", "Accept, Origin, Content-type,authorization")
                        .WithHeader("Access-Control-Max-Age", maxAge.ToString());
                }
            });
        }
    }

    public class PageNotFoundHandler : DefaultViewRenderer, IStatusCodeHandler
    {
        public PageNotFoundHandler(IViewFactory factory) : base(factory)
        {
        }

        public bool HandlesStatusCode(HttpStatusCode statusCode, NancyContext context)
        {
            return statusCode == HttpStatusCode.NotFound ||
                statusCode == HttpStatusCode.Unauthorized;
        }

        public void Handle(HttpStatusCode statusCode, NancyContext context)
        {
            dynamic model = new ExpandoObject();
            model.Url = context.Request.Url;
            Response response = null;
            switch (statusCode)
            {
                case HttpStatusCode.NotFound:
                    response = RenderView(context, "404", model);
                    response.StatusCode = HttpStatusCode.NotFound;
                    break;
                case HttpStatusCode.Unauthorized:
                    response = RenderView(context, "401", model);
                    response.StatusCode = HttpStatusCode.Unauthorized;
                    break;
            }

            if (response != null)
            {
                context.Response = response;
            }
        }
    }

    public class CustomJsonSerializer : JsonSerializer
    {
        public CustomJsonSerializer()
        {
            //this.ContractResolver = new CamelCasePropertyNamesContractResolver();//首字母小写
            this.Formatting = Formatting.Indented;
            this.DateFormatString = "yyyy-MM-dd HH:mm:ss";
            //this.PreserveReferencesHandling = PreserveReferencesHandling.Objects;//保存引用类型
            this.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
            this.NullValueHandling = NullValueHandling.Include;
            this.ContractResolver = new NullToEmptyStringResolver();

        }
    }

    public class NullToEmptyStringResolver : DefaultContractResolver
    {
        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            return type.GetProperties().Select(property => CreateProperty(property, memberSerialization)).ToList();
        }

        private JsonProperty CreateProperty(PropertyInfo property, MemberSerialization memberSerialization)
        {
            var jsonProperty = base.CreateProperty(property, memberSerialization);

            jsonProperty.ValueProvider = new NullToEmptyStringValueProvider(jsonProperty.ValueProvider, property);

            return jsonProperty;
        }

        private class NullToEmptyStringValueProvider : IValueProvider
        {
            private readonly IValueProvider _valueProvider;

            private readonly PropertyInfo _property;

            public NullToEmptyStringValueProvider(IValueProvider valueProvider, PropertyInfo property)
            {
                _valueProvider = valueProvider;
                _property = property;
            }

            public object GetValue(object target)
            {
                var result = _valueProvider == null
                    ? _property.GetValue(target)
                    : _valueProvider.GetValue(target);

                if (_property.PropertyType == typeof(string) && result == null)
                {
                    result = "";
                }
                return result;
            }

            public void SetValue(object target, object value)
            {
                if (_valueProvider == null)
                {
                    _property.SetValue(target, value);
                }
                else
                {
                    _valueProvider.SetValue(target, value);
                }
            }
        }
    }

}